package org.windowkit.toolkit.widget;

import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

import javax.swing.Action;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.JTextComponent;
import javax.swing.text.TextAction;

/**
 * A popup menu that contains all the edit actions:
 * <ul>
 * <li>Cut</li>
 * <li>Copy</li>
 * <li>Paste</li>
 * <li>Clear</li>
 * <li>Undo</li>
 * <li>Select All</li>
 * </ul>
 * The menu listens to itself and will take care of enabling/disabling
 * actions when the menu is displayed.
 * <p>
 * The static configure methods will add the the menu items to the JPopupMenu they
 * are given.
 * <p>
 * Right now all this edit functionality is very inconsistent and buggy throughout the
 * app.  If the client ever wants it fixed in the future then the two legacy popup
 * menus(EditFieldPopup, MJEditMenu) can be replaced with this class for consistency.
 * Right now they only delagate to this class for cut/copy/paste because the legacy
 * implementation used a sandbox clipboard(actually a JTextArea).
 * <p>
 * <strong>NOTE: Currently we don't have the policy files in place to access the System
 * clipboard so we can't check it's contents when deciding if we should enable paste or not.
 * So we just always enable it.</strong>
 *
 *
 * @author Rich O'Connell
 *
 */
public class EditMenu extends JPopupMenu  {

    /**
	 *
	 */
	private static final long serialVersionUID = -1924238768701155258L;
	private static final String CUT = "Cut";
    private static final String COPY = "Copy";
    private static final String PASTE = "Paste";
    public static final String UNDO = "Undo";
    private static final String SELECT_ALL = "Select All";
    private static final String CLEAR = "Clear";

    /* Menu Actions */
    static Action cutAction;
    static Action copyAction;
    static Action pasteAction;
    static Action clearAction;
    static Action undoAction;
    static Action selectAllAction;

    /* Initiate our actions */
    static {

        clearAction = new ClearAction();
        undoAction = new UndoAction();
        selectAllAction = new SelectAllAction();

        /* Get the cut, copy, and paste actions from the DefaultEditorKit */
        DefaultEditorKit dek = new DefaultEditorKit();
        for (Action a : dek.getActions()) {

            if (a.getValue(Action.NAME).equals(DefaultEditorKit.copyAction)) {
                copyAction = a;
            } else if (a.getValue(Action.NAME).equals(
                    DefaultEditorKit.cutAction)) {
                cutAction = a;
            } else if (a.getValue(Action.NAME).equals(
                    DefaultEditorKit.pasteAction)) {
                pasteAction = a;
            }
        }
    }

    /**
     * Constructs a standard edit menu with undo/cut/copy/paste/clear items.
     */
    public EditMenu() {
        configureEditPopupMenu(this);
    }

    /**
     * Adds the Cut/Copy/Paste actions to the given JPopupMenu.
     *
     * @param menu
     *          The menu to add the items to.
     */
    public static void configureEditPopupMenuSlim(final JPopupMenu menu){

        final JMenuItem mnuCut = createCutItem();
        menu.add(mnuCut);

        final JMenuItem mnuCopy = createCopyItem();
        menu.add(mnuCopy);

        final JMenuItem mnuPaste = createPasteItem();
        menu.add(mnuPaste);

        // we need to listen for the menu being shown
        menu.addPopupMenuListener(new EditMenuListener(menu));

    }

    public static void configureEditPopupMenu(final JPopupMenu menu){

        final JMenuItem mnuUndo = createUndoItem();
        menu.add(mnuUndo);
        menu.addSeparator();

        final JMenuItem mnuCut = createCutItem();
        menu.add(mnuCut);

        final JMenuItem mnuCopy = createCopyItem();
        menu.add(mnuCopy);

        final JMenuItem mnuPaste = createPasteItem();
        menu.add(mnuPaste);
        menu.addSeparator();

        final JMenuItem mnuClear = new JMenuItem(clearAction);
        menu.add(mnuClear);

        final JMenuItem mnuSelectAll = new JMenuItem(selectAllAction);
        menu.add(mnuSelectAll);

        // we need to listen for the menu being shown
        menu.addPopupMenuListener(new EditMenuListener(menu));
    }



    /**
     * @return
     */
    private static JMenuItem createUndoItem() {
        final JMenuItem mnuUndo = new JMenuItem(undoAction);
        mnuUndo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z,
                ActionEvent.CTRL_MASK));
        return mnuUndo;
    }

    /**
     * @return
     */
    private static JMenuItem createPasteItem() {
        final JMenuItem mnuPaste = new JMenuItem(PASTE);
        mnuPaste.addActionListener(pasteAction);
        mnuPaste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V,
                ActionEvent.CTRL_MASK));
        mnuPaste.setMnemonic(KeyEvent.VK_P);
        return mnuPaste;
    }

    /**
     * @return
     */
    private static JMenuItem createCopyItem() {
        final JMenuItem mnuCopy = new JMenuItem(COPY);
        mnuCopy.addActionListener(copyAction);
        mnuCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
                ActionEvent.CTRL_MASK));
        mnuCopy.setMnemonic(KeyEvent.VK_C);
        return mnuCopy;
    }

    /**
     * @return
     */
    private static JMenuItem createCutItem() {
        final JMenuItem mnuCut = new JMenuItem(CUT);
        mnuCut.addActionListener(cutAction);
        mnuCut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,
                ActionEvent.CTRL_MASK));
        mnuCut.setMnemonic(KeyEvent.VK_T);
        return mnuCut;
    }


    /**
     * Get the focused JTextComponent.
     * Returns <code>null</code> if the focused component is not a JTextComponent.
     *
     * @return
     */
    static JTextComponent getFocusedComponent() {
        Component rv = KeyboardFocusManager.getCurrentKeyboardFocusManager()
        .getPermanentFocusOwner();
        if (rv != null && rv instanceof JTextComponent) {
            return (JTextComponent) rv;
        }
        return null;
    }

    /*
     * We listen for the menu to be shown so we can check what functionality to
     * enable. The actions this menu supports actually take care of these checks
     * by themselves, a paste for instance will not paste to an udeditable
     * component even if we allow it to be called from this menu. We are
     * actually just enabling/disabling the menu items for display purposes.
     */
    private static class EditMenuListener implements PopupMenuListener{

        JMenuItem[] menuItems;

        public EditMenuListener(final JPopupMenu pop) {
            ArrayList<JMenuItem> l = new ArrayList<JMenuItem>();
            for(Component c : pop.getComponents()){
                if (c instanceof JMenuItem) {
                    JMenuItem mi = (JMenuItem) c;
                    l.add(mi);
                }
            }
            menuItems = l.toArray(menuItems = new JMenuItem[l.size()]);
        }

        private void setActionEnabled(final String actionName, final boolean enable ){
            for(JMenuItem mi : menuItems){
                if(mi.getText().equals(actionName)){
                    mi.setEnabled(enable);
                }
            }
        }

        private void setAllEnabled(final boolean enable){
            for(JMenuItem mi : menuItems){
                mi.setEnabled(enable);
            }
        }

        @SuppressWarnings("unused")
        public void popupMenuWillBecomeVisible(final PopupMenuEvent e) {

            JTextComponent focused = getFocusedComponent();

            //  debug
//          System.out.println("Perm Focus Owner: "
//          + (focused != null ? focused.getText() : "") + " -- "
//          + focused);

            if (focused != null) {

                // check if the component supports undo and has an undo
                // available
//                if (focused instanceof MJTextUndo) {
//                    MJTextUndo undoable = (MJTextUndo) focused;
//                    if (undoable.hasundo()) {
//                        setActionEnabled(UNDO, true);
//                    } else {
//                        setActionEnabled(UNDO, false);
//                    }
//                } else {
                    setActionEnabled(UNDO, false);
//                }

                // only enable if there is a selection
                if (focused.getSelectedText() != null) {
                    setActionEnabled(CUT, true);
                    setActionEnabled(COPY, true);
                    setActionEnabled(CLEAR, true);
                } else {
                    setActionEnabled(CUT, false);
                    setActionEnabled(COPY, false);
                    setActionEnabled(CLEAR, false);
                }

                // only enable if paste if the clipboard has something on it
                // As an applet we can only access the clipboard if we have
                // permission. Right now the client is unwilling to set up policy
                // files to give themselves this permission so we will just have
                // to allways enable paste.
                boolean enablePaste = true;
//                try {
//                    enablePaste = (Toolkit.getDefaultToolkit()
//                            .getSystemClipboard().getContents(null) != null);
//                } catch (AccessControlException exception) {
//                    System.err.println("Acess Control Exception while checking "
//                            + "contents of clipboard.");
//                    enablePaste = true;
//                }
                setActionEnabled(PASTE, enablePaste);

                // allways enabled? sure, why not
                setActionEnabled(SELECT_ALL, true);

            } else {
                // This menu only supports operations on JTextComponents
                setAllEnabled(false);
            }

        }

        public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) {}
        public void popupMenuCanceled(final PopupMenuEvent e) {}

    }



    /**
     * The 'Undo' action.
     * Will call undo on the focused text component, if it supports MJTextUndo.
     *
     */
    public static class UndoAction extends TextAction {



        /**
		 *
		 */
		private static final long serialVersionUID = -1471741637678591305L;

		public UndoAction() {
            super(UNDO);
        }

        public void actionPerformed(final ActionEvent e) {
            getTextComponent(e);
        }
    }

    /**
     * The 'Clear' action.
     * Clears out the focused text component.
     */
    public static class ClearAction extends TextAction {



        /**
		 *
		 */
		private static final long serialVersionUID = 7253465626145112175L;

		public ClearAction() {
            super(CLEAR);
        }

        public void actionPerformed(final ActionEvent e) {
            JTextComponent comp = getTextComponent(e);
            if (comp != null) {
                comp.setText("");
            }
        }
    }

    /**
     * The 'Select All' action.
     * Will select all the text of the focused text component.
     *
     */
    public static class SelectAllAction extends TextAction {
        /**
		 *
		 */
		private static final long serialVersionUID = 1036976748763313472L;

		public SelectAllAction() {
            super(SELECT_ALL);
        }

        public void actionPerformed(final ActionEvent e) {
            JTextComponent comp = getTextComponent(e);
            if (comp != null) {
                comp.selectAll();
            }
        }
    }


}
